package com.fw.leetcode.doublepointer;

import com.fw.leetcode.LeetCode;

import java.util.Arrays;

/**
 * 88. Merge Sorted Array
 *
 * You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n,
 * representing the number of elements in nums1 and nums2 respectively.
 *
 * Merge nums1 and nums2 into a single array sorted in non-decreasing order.
 *
 * The final sorted array should not be returned by the function, but instead be stored inside the array nums1.
 * To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should
 * be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n.
 *
 * Example 1:
 *  Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
 *  Output: [1,2,2,3,5,6]
 *  Explanation: The arrays we are merging are [1,2,3] and [2,5,6].
 *               The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1.
 *
 * Example 2:
 *  Input: nums1 = [1], m = 1, nums2 = [], n = 0
 *  Output: [1]
 *  Explanation: The arrays we are merging are [1] and [].
 *               The result of the merge is [1].
 *
 * Example 3:
 *  Input: nums1 = [0], m = 0, nums2 = [1], n = 1
 *  Output: [1]
 *  Explanation: The arrays we are merging are [] and [1].
 *               The result of the merge is [1].
 *               Note that because m = 0, there are no elements in nums1.
 *               The 0 is only there to ensure the merge result can fit in nums1.
 *
 * Constraints:
 *  nums1.length == m + n
 *  nums2.length == n
 *  0 <= m, n <= 200
 *  1 <= m + n <= 200
 *  -10^9 <= nums1[i], nums2[j] <= 10^9
 *
 * Follow up: Can you come up with an algorithm that runs in O(m + n) time?
 */
public class Num_0088 implements LeetCode {
    private interface Solution {
        void merge(int[] nums1, int m, int[] nums2, int n);

        default void assertEquals(int[] nums1, int[] nums2, int[] expected) {
            int[] nums1Expand = new int[nums1.length + nums2.length]; // 后 n2 位默认为0
            System.arraycopy(nums1, 0, nums1Expand, 0, nums1.length);
            merge(nums1Expand, nums1.length, nums2, nums2.length);
            if (!Arrays.equals(nums1Expand, expected)) {
                nums1Expand = new int[nums1.length + nums2.length]; // 后 n2 位默认为0
                System.arraycopy(nums1, 0, nums1Expand, 0, nums1.length);
                merge(nums1Expand, nums1.length, nums2, nums2.length);
            }
        }
    }

    private static class MySolution implements Solution {

        @Override
        public void merge(int[] nums1, int m, int[] nums2, int n) { // 逆向双指针：时O(m+n) 空O(1)
            /*
             * 合并两个数组（nums1原位） nums1+nums2->nums1
             */
            int idx = nums1.length - 1;
            int idx1 = m - 1;
            int idx2 = n - 1;
            // 从后向前遍历
            while (idx1 >= 0 && idx2 >= 0) {
                nums1[idx--] = nums1[idx1] > nums2[idx2] ? nums1[idx1--] : nums2[idx2--];
            }
            while (idx1 >= 0) {
                nums1[idx--] = nums1[idx1--];
            }
            while (idx2 >= 0) {
                nums1[idx--] = nums2[idx2--];
            }
        }
    }

    public static void main(String[] args) {
        Solution solution = new MySolution();
        solution.assertEquals(new int[]{1,2,3}, new int[]{2,5,6}, new int[]{1,2,2,3,5,6});
        solution.assertEquals(new int[]{1}, new int[]{}, new int[]{1});
        solution.assertEquals(new int[]{}, new int[]{1}, new int[]{1});
    }
}
